package app.framework.front.util;

import jasmine.framework.common.util.CheckUtil;
import jasmine.framework.common.util.CollectionUtil;
import jasmine.framework.common.util.ErrorUtil;
import jasmine.framework.common.util.JsonUtil;
import jasmine.framework.common.util.ResourceUtil;
import jasmine.framework.common.util.StringUtil;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * <p>
 * URL 映射规则工具类。
 * </p>
 *
 * @author mh.z
 */
public class UrlMappingUtil {
    private static final PathMatchingResourcePatternResolver PATH_RESOLVER;

    private static final String SYMBOL_BACKSLASH = "/";
    private static final String SYMBOL_DOT = ".";

    static {
        PATH_RESOLVER = new PathMatchingResourcePatternResolver();
    }

    /**
     * 返回 URL 映射
     *
     * @param configPath
     * @return
     */
    public static Map<String, String> getMappingUrlMap(String configPath) {
        CheckUtil.notNull(configPath, "configPath null");

        // 读取配置
        String json = ResourceUtil.getStringFromPath(configPath);
        if (StringUtil.isBlank(json)) {
            return Collections.emptyMap();
        }

        // 解析配置
        List<UrlMappingRule> ruleList = JsonUtil.fromArray(json, UrlMappingRule.class);
        if (CollectionUtil.isEmpty(ruleList)) {
            return Collections.emptyMap();
        }

        Map<String, String> mappingUrlMap = new LinkedHashMap<>();
        for (UrlMappingRule rule : ruleList) {
            // 添加 URL 映射
            addMappingUrlMap(mappingUrlMap, rule);
        }

        return mappingUrlMap;
    }

    /**
     * 添加 URL 映射
     *
     * @param resultMap
     * @param rule
     */
    protected static void addMappingUrlMap(Map<String, String> resultMap, UrlMappingRule rule) {
        CheckUtil.notNull(resultMap, "resultMap null");
        CheckUtil.notNull(rule, "rule null");
        String originalUrlPrefix = CheckUtil.notNull(rule.getOriginalUrlPrefix(),
                "rule.originalUrlPrefix null");
        String mappedUrlPrefix = CheckUtil.notNull(rule.getMappedUrlPrefix(),
                "rule.mappedUrlPrefix null");
        String resourceBasePath = CheckUtil.notNull(rule.getResourceBasePath(),
                "rule.resourceBasePath null");
        boolean minSuffixPriority = Boolean.TRUE.equals(rule.getMinSuffixPriority());

        String locationPattern = (resourceBasePath.endsWith(SYMBOL_BACKSLASH))
                ? (resourceBasePath + "**/*")
                : (resourceBasePath + "/**/*");
        Resource[] scannedResources;
        Resource basePathResource;
        int basePathLength;

        try {
            basePathResource = PATH_RESOLVER.getResource(resourceBasePath);
            basePathLength = getUriLength(basePathResource);
            // 扫描指定的文件
            scannedResources = PATH_RESOLVER.getResources(locationPattern);
        } catch (IOException e) {
            throw ErrorUtil.sneakyError(e);
        }

        Set<String> relativePathSet = new LinkedHashSet<>();
        for (Resource resource : scannedResources) {
            if (!resource.isReadable()) {
                continue;
            }

            String resourcePath = getUriPath(resource);
            String relativePath = resourcePath.substring(basePathLength);
            // 把扫描到的文件的相对路径添加到集合中
            relativePathSet.add(relativePath);
        }

        for (String relativePath : relativePathSet) {
            String minUrl = null;

            if (minSuffixPriority) {
                // 获取对应的压缩路径
                String minRelativePath = getMinPath(relativePath);

                // 如果有这个压缩路径对应的文件则取这个压缩路径作为映射值
                if (relativePathSet.contains(minRelativePath)) {
                    minUrl = getFullPath(mappedUrlPrefix, minRelativePath);
                }
            }

            // 拼接 URL 路径
            String originUrl = getFullPath(originalUrlPrefix, relativePath);
            String mappedUrl = (minUrl == null)
                    ? getFullPath(mappedUrlPrefix, relativePath)
                    : minUrl;

            // 添加 URL 映射
            resultMap.put(originUrl, mappedUrl);
        }
    }

    /**
     * 返回压缩路径
     *
     * @param path
     * @return
     */
    protected static String getMinPath(String path) {
        CheckUtil.notNull(path, "path null");

        int backslashIndex = path.lastIndexOf(SYMBOL_BACKSLASH);
        if (backslashIndex < 0) {
            backslashIndex = 0;
        }

        // 不处理文件名中已经带有 ".min." 字符串的路径
        if (path.indexOf(".min.", backslashIndex) >= 0) {
            return path;
        }

        int dotIndex = path.lastIndexOf(SYMBOL_DOT);
        // 不处理文件名中不带有 "." 符号的路径
        if (dotIndex < backslashIndex) {
            return path;
        }

        StringBuilder buffer = new StringBuilder();
        buffer.append(path.substring(0, dotIndex));
        buffer.append(".min");
        buffer.append(path.substring(dotIndex));

        return buffer.toString();
    }

    /**
     * 返回完整路径
     *
     * @param basePath
     * @param relativePath
     * @return
     */
    protected static String getFullPath(String basePath, String relativePath) {
        CheckUtil.notNull(basePath, "basePath null");
        CheckUtil.notNull(relativePath, "relativePath null");
        StringBuilder buffer = new StringBuilder();

        buffer.append(basePath);
        if (!basePath.endsWith(SYMBOL_BACKSLASH)) {
            buffer.append(SYMBOL_BACKSLASH);
        }

        if (relativePath.startsWith(SYMBOL_BACKSLASH)) {
            buffer.append(relativePath.substring(1));
        } else {
            buffer.append(relativePath);
        }

        return buffer.toString();
    }

    /**
     * 返回 URI 路径
     *
     * @param resource
     * @return
     */
    protected static String getUriPath(Resource resource) {
        CheckUtil.notNull(resource, "resource null");
        String path;

        try {
            URI uri = resource.getURI();
            path = uri.toString();
        } catch (IOException e) {
            throw ErrorUtil.sneakyError(e);
        }

        return path;
    }

    /**
     * 返回 URI 长度
     *
     * @param resource
     * @return
     */
    protected static int getUriLength(Resource resource) {
        CheckUtil.notNull(resource, "resource null");
        String path = getUriPath(resource);

        return path.length();
    }

}
